package researchgrants.parts.GrantRequest;

import java.awt.Window;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import researchgrants.parts.GrantRequest.agency.AgencyContact;
import researchgrants.parts.GrantRequest.grantOfficer.GrantOfficer;
import researchgrants.parts.GrantRequest.perennialStipends.PerennialStipends;
import researchgrants.parts.GrantRequest.statusChanges.GrantRequestStatusChange;
import researchgrants.parts.GrantRequest.statusChanges.GrantRequestStatusChangeList;
import researchgrants.parts.LoggedData.LoggedData;
import researchgrants.parts.date.LoggedDate;
import researchgrants.parts.db.Db;
import researchgrants.parts.GrantRequest.expensePurpose.ExpensePurpose;
import researchgrants.parts.GrantRequest.nsfcode.NsfCode;
import researchgrants.parts.GrantRequest.reminder.Reminder;
import researchgrants.parts.pi.PisList;

/*
 * This class holds all data of a single Grant Request.
 * It should hold all the logic of GrantRequest. Currently, adding a GrantRequest is in GrantRequestView. TODO: Change that.
 * It allows users of this class to retrieve the different LoggedData fields and their current values.
 * 
 */
public class GrantRequest {

    // retreives an existing grant request
    static GrantRequest getById(int id) {
        return (new GrantRequest(id));
    }

    // opens a window to input the user for a new grant request
    static GrantRequest inputNew(Window parent) {
        // initializes an unbound Grant Request View
        GrantRequestView viewNew = new GrantRequestView(parent);
        GrantRequest newGrantRequest=viewNew.getGrantRequest();
        return (newGrantRequest);
    }


    // the identification field of the grant request
    private int id;
    
    // accompanying data
    private LoggedData title;
    private LoggedData deadline;
    private LoggedData firstContact;
    private LoggedData pis;
    private LoggedData expensePurpose;
    private LoggedData grantOfficer;
    private LoggedData nsfCode;
    private LoggedData notes;
    private LoggedData agencyContact;
    private PerennialStipends expectedPerennialStipends;
    private LoggedData reminder;
    
    // also, the accompanying status changes for this grant request
    GrantRequestStatusChangeList statusChangeList;

    // An outside method for refreshing a GrantRequest. Gets called when an inner value changes from within (through LoggedDataManagers and LoggedDataPanels, and StatusChange updates).
    public void refreshData() {
        syncDataFromDb();
    }

    private void syncDataFromDb() {
        try {
            Connection conn=Db.openDbConnection();
            PreparedStatement stat = Db.createPreparedStatement(conn, "SELECT TitleRef, DeadlineRef, FirstContactRef, PisRef, ExpensePurposeRef, GrantOfficerRef, NsfCodeRef, NotesRef, AgencyContactRef, ExpectedPerennialStipendsID, ReminderRef FROM TblGrantRequests Where GrantRequestID=?");
            stat.setInt(1, id);
            stat.executeQuery();
            ResultSet rs=stat.getResultSet();
            Db.moveToFirstLine(rs);

            title=LoggedData.getByID((Integer) Db.getColumn(rs, 1));
            deadline=LoggedData.getByID((Integer) Db.getColumn(rs, 2));
            firstContact=LoggedData.getByID((Integer) Db.getColumn(rs, 3));
            pis=LoggedData.getByID((Integer) Db.getColumn(rs, 4));
            expensePurpose=LoggedData.getByID((Integer) Db.getColumn(rs, 5));
            grantOfficer=LoggedData.getByID((Integer) Db.getColumn(rs, 6));
            nsfCode=LoggedData.getByID((Integer) Db.getColumn(rs, 7));
            notes=LoggedData.getByID((Integer) Db.getColumn(rs, 8));
            agencyContact=LoggedData.getByID((Integer) Db.getColumn(rs, 9));
            expectedPerennialStipends=PerennialStipends.getByID((Integer) Db.getColumn(rs, 10));
            reminder=LoggedData.getByID((Integer) Db.getColumn(rs, 11));

            rs.close();
            stat.close();
            Db.closeDbConnection(conn);
            
        } catch (SQLException ex) {
            Logger.getLogger(LoggedData.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            throw new IllegalArgumentException("Problem with Data: " + ex.getLocalizedMessage());
        }
        
        statusChangeList=GrantRequestStatusChangeList.getGrantRequestStatusChangeSetForGrantRequest(id);
    }

    private GrantRequest(int grantId) {
        this.id = grantId;
//        syncDataFromDb();
    }

    // displays the grant request for the user. allows editing the content.
    void show(Window parent) {
        new GrantRequestView(parent, this);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final GrantRequest other = (GrantRequest) obj;
        return(other.id==id);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + this.id;
        return hash;
    }
    
    
    public int getId() {
        return(id);
    }
    
    // returns the last status change - the current status
    public GrantRequestStatusChange getLastStatusChange() {
        syncDataFromDb();
        Iterator<GrantRequestStatusChange> iteratorStatusChangeList = statusChangeList.iterator();
        GrantRequestStatusChange grantRequestStatusChange=null;
        while (iteratorStatusChangeList.hasNext()) {
            grantRequestStatusChange = iteratorStatusChangeList.next();
        }
        return(grantRequestStatusChange);
    }
    
    
    /*
     * allows retrieval of all different fields
     */
    
    public String getCurrentTitle() {
        syncDataFromDb();
        return(title.value());
    }
    
    public LoggedDate getCurrentDeadline() {
        syncDataFromDb();
        final String value = deadline.value();
        if (value.equals(""))  {
            return(null);
        } else {
            return(LoggedDate.fromLoggedData(value));
        }
    }
    
    public LoggedDate getCurrentFirstContact() {
        syncDataFromDb();
        final String value = firstContact.value();
        if (value.equals(""))  {
            return(null);
        } else {
            return(LoggedDate.fromLoggedData(value));
        }
    }
    
    public PisList getCurrentPis() {
        syncDataFromDb();
        return(PisList.fromLoggedData(pis.value()));
    }
    
    public ExpensePurpose getCurrentExpensePurpose() {
        syncDataFromDb();
        final String value = expensePurpose.value();
        if (value.equals("")) {
            return(null);
        } else {
            return(ExpensePurpose.fromLoggedData(value));
        }
    }
    
    public GrantOfficer getCurrentGrantOfficer() {
        syncDataFromDb();
        final String value = grantOfficer.value();
        if (value.equals("")) {
            return(null);
        } else {
            return(GrantOfficer.fromLoggedData(value));
        }
    }
    
    public PerennialStipends getExpectedPerennialStipends() {
        syncDataFromDb();
        return(expectedPerennialStipends);
    }
    
    public NsfCode getCurrentNsfCode() {
        syncDataFromDb();
        final String value = nsfCode.value();
        if (value.equals("")) {
            return(null);
        } else {
            return(NsfCode.fromLoggedData(value));
        }
    }
    
    public String getCurrentNotes() {
        syncDataFromDb();
        return(notes.value());
    }
    
    public AgencyContact getCurrentAgencyContact() {
        syncDataFromDb();
        final String value = agencyContact.value();
        if (value.equals("")) {
            return(null);
        } else {
            return(AgencyContact.fromLoggedData(value));
        }
    }

    public Reminder getCurrentReminder() {
        syncDataFromDb();
        if (reminder.value().equals("")) {
            return(null);
        } else {
            return(Reminder.getById(Integer.parseInt(reminder.value())));
        }
    }
    
    /*
     * allows retrieval of all raw different fields - to be used by Panels for editing and saving changes.
     */
    

    LoggedData getLoggedDataAgencyContact() {
        syncDataFromDb();
        return(agencyContact);
    }

    LoggedData getLoggedDataDeadline() {
        syncDataFromDb();
        return(deadline);
    }

    LoggedData getLoggedDataExpensePurpose() {
        syncDataFromDb();
        return(expensePurpose);
    }

    LoggedData getLoggedDataFirstContact() {
        syncDataFromDb();
        return(firstContact);
    }

    LoggedData getLoggedDataGrantOfficer() {
        syncDataFromDb();
        return(grantOfficer);
    }

    LoggedData getLoggedDataNotes() {
        syncDataFromDb();
        return(notes);
    }

    LoggedData getLoggedDataNsfCode() {
        syncDataFromDb();
        return(nsfCode);
    }

    LoggedData getLoggedDataPis() {
        syncDataFromDb();
        return(pis);
    }

    LoggedData getLoggedDataTitle() {
        syncDataFromDb();
        return(title);
    }
    
    LoggedData getLoggedDataReminder() {
        syncDataFromDb();
        return(reminder);
    }

    /**
     * Associate a newly saved <code>Reminder</code> with this <code>GrantRequest</code>
     * Invoked from the reminder panels
     * @param newReminder the already created new reminder to associate
     */
    public void saveNewReminder(Reminder newReminder) {
        reminder.setNewValue(Integer.toString(newReminder.getId()));
    }

    /**
     * Cancels the <code>Reminder</code> associated with this <code>GrantRequest</code>.
     * Invoked from the reminder panels
     */
    public void cancelReminder() {
        if (reminder.value().equals("")) {
            throw new RuntimeException("The GrantRequest Didn't have a reminder to cancel");
        }
        reminder.setNewValue("");
    }
    
}
